package com.flyqiu.common.tree;


import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.flyqiu.common.tools.FlyQiuCollectionTools;
import org.apache.commons.lang3.StringUtils;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 树型数据功能块
 */
public class FlyQiuTreeTools {
    /**
     * 创建树结构
     *
     * @param models 待转换成树结构的数据
     * @param <T>    类型
     * @return 树形结构数据
     */
    public static <T extends FlyQiuTreeModel<T>> List<T> createTree(List<T> models) {
        return createTree(models, null, true);
    }

    /**
     * 创建树结构
     *
     * @param models      待转换成树结构的数据
     * @param containRoot 是否包含root 节点
     * @param <T>         类型
     * @return 树形结构数据
     */
    public static <T extends FlyQiuTreeModel<T>> List<T> createTree(List<T> models, boolean containRoot) {
        return createTree(models, null, containRoot);
    }

    /**
     * @param models      待转换成树结构的数据
     * @param rootIds     root节点id
     * @param containRoot 是否包含root 节点
     * @param <T>         类型
     * @return 树形结构数据
     */
    public static <T extends FlyQiuTreeModel<T>> List<T> createTree(List<T> models, Collection<String> rootIds, boolean containRoot) {
        if (models == null || models.isEmpty()) return new ArrayList<>();
        List<T> rootNodes = findRoots(models, rootIds);
        Map<String, List<T>> listMap = models.stream().filter(e -> e.getParentId() != null).collect(Collectors.groupingBy(T::getParentId));
        for (T model : models) {
            List<T> children = listMap.get(model.getId());
            model.setChildren(children);
        }
        if (containRoot) {
            return rootNodes;
        }
        List<T> treeModels = new ArrayList<>();
        for (T model : rootNodes) {
            if (model.getChildren() == null || model.getChildren().isEmpty()) {
                continue;
            }
            List<T> children = model.getChildren();
            treeModels.addAll(children);
        }
        return treeModels;
    }

    /**
     * 查找全部父级
     *
     * @param models 集合数据
     * @param <T>    类型
     * @return root节点集合
     */
    public static <T extends FlyQiuTreeModel<T>> List<T> findRoots(List<T> models) {
        return findRoots(models, null);
    }

    /**
     * 查找全部父级
     *
     * @param models  集合数据
     * @param rootIds 指定父级 节点id
     * @param <T>     类型
     * @return root节点集合
     */
    public static <T extends FlyQiuTreeModel<T>> List<T> findRoots(List<T> models, Collection<String> rootIds) {
        if (models.isEmpty()) return new ArrayList<>();
        if (rootIds != null && !rootIds.isEmpty()) {
            return models.stream().filter(e -> rootIds.contains(e.getId())).collect(Collectors.toList());
        }

        List<T> rootNodes = new ArrayList<>();
        Set<String> dicMap = new HashSet<>();
        for (T model : models) {
            if (StringUtils.isBlank(model.getId())) continue;
            dicMap.add(model.getId());
        }
        for (T model : models) {
            if (StringUtils.isNotEmpty(model.getParentId()) && dicMap.contains(model.getParentId())) {
                continue;
            }
            rootNodes.add(model);
        }
        return rootNodes;
    }

    /**
     * 查找当前节点的全部父级节点
     *
     * @param models 集合
     * @param nodeId 节点id
     * @param <T>    数据类型
     * @return 树形结构数据
     */
    public static <T extends FlyQiuTreeModel<T>> List<T> findParents(List<T> models, String nodeId) {
        if (models == null || models.isEmpty()) return new ArrayList<>();
        if (StringUtils.isEmpty(nodeId)) return new ArrayList<>();
        List<T> modelNodes = models.stream().filter(e -> e.getId().equals(nodeId)).toList();
        if (modelNodes.isEmpty()) return new ArrayList<>();
        T tempNode = modelNodes.get(0);
        return findParents(models, tempNode);
    }

    /**
     * 查找当前节点的全部父级节点
     *
     * @param models 集合
     * @param node   节点
     * @return 树形结构数据
     */
    public static <T extends FlyQiuTreeModel<T>> List<T> findParents(List<T> models, T node) {
        if (node == null) return new ArrayList<>();
        T tempNode = node;
        List<T> parentNodes = new ArrayList<>();
        while (StringUtils.isNotEmpty(tempNode.getParentId())) {
            T finalTempNode = tempNode;
            List<T> tempNodesTemp = models.stream().filter(e -> e.getId().equals(finalTempNode.getParentId())).toList();
            if (tempNodesTemp.isEmpty()) break;
            tempNode = tempNodesTemp.get(0);
            parentNodes.add(tempNode);
        }
        Collections.reverse(parentNodes);
        return parentNodes;
    }

    /**
     * 计算树的最大深度。
     * @param tree 树的节点列表，每个节点都实现SimpleTreeModel接口。
     * @param <T> SimpleTreeModel的子类型。
     * @return 树的最大深度。
     * @throws IllegalArgumentException 如果tree参数为null。
     */
    public static <T extends FlyQiuTreeModel<T>> int getTreeMaxLevel(List<T> tree) throws IllegalArgumentException {
        // 参数检查
        if (tree == null) {
            throw new IllegalArgumentException("Tree cannot be null.");
        }

        int maxLevel = 0;
        for (T item : tree) {
            // 对null的额外检查已不必要，因为下面的getChildren()调用前已经有了null检查。
            List<T> children = item.getChildren();
            if (children == null || children.isEmpty()) {
                continue;
            }
            // 递归计算子树的最大深度
            int itemMaxLevel = getTreeMaxLevel(children);
            maxLevel = Math.max(itemMaxLevel, maxLevel);
        }
        // 加1以考虑当前层级
        return maxLevel + 1;
    }

    /**
     * 树形数据转list
     *
     * @param models 原始树形数据
     * @param <T>    数据类型
     * @return list结构数据
     */
    public static <T extends FlyQiuTreeModel<T>> List<T> treeToList(List<T> models) {
        List<T> result = new ArrayList<>();
        if (FlyQiuCollectionTools.isEmpty(models)) {
            return result;
        }
        treeToList(models, result);
        return result;
    }

    /**
     * 树形数据转list
     *
     * @param models  原始树形数据
     * @param outList 输出list
     * @param <T>     数据类型
     */
    public static <T extends FlyQiuTreeModel<T>> void treeToList(List<T> models, List<T> outList) {
        if (outList == null) {
            throw new RuntimeException("outList 不能为空");
        }
        if (FlyQiuCollectionTools.isEmpty(models)) {
            return;
        }
        for (T model : models) {
            outList.add(model);
            if (FlyQiuCollectionTools.isEmpty(model.getChildren())) {
                continue;
            }
            treeToList(model.getChildren(), outList);
        }
    }

    public static JSONArray treeToList(JSONArray array) {
        return treeToList(array, "id", "parentId", "children", null);
    }

    /**
     * json 树型结构数据转数组
     *
     * @param array       集合
     * @param valueKey    值键
     * @param childrenKey 子级键
     * @param parentKey   父级键
     * @return JSONArray
     */
    public static JSONArray treeToList(JSONArray array, String valueKey, String parentKey, String childrenKey, String parentId) {
        JSONArray result = new JSONArray();
        treeToList(array, valueKey, parentKey, childrenKey, parentId, result::add);
        return result;
    }

    /**
     * json 树型结构数据转数组
     *
     * @param array       集合
     * @param valueKey    值键
     * @param childrenKey 子级键
     * @param parentKey   父级键
     * @param parentId    父级id
     * @param callBack    回调
     */
    public static void treeToList(JSONArray array, String valueKey, String parentKey, String childrenKey, String parentId, TreeDataFindChildrenItem<JSONObject> callBack) {
        if (array.isEmpty()) return;
        valueKey = StringUtils.isEmpty(valueKey) ? "id" : valueKey;
        childrenKey = StringUtils.isEmpty(childrenKey) ? "children" : childrenKey;
        parentKey = StringUtils.isEmpty(parentKey) ? "parentKey" : parentKey;

        for (int i = 0; i < array.size(); i++) {
            JSONObject item = array.getJSONObject(i);
            callBack.findItem(item);
            if (!StringUtils.isEmpty(parentId) && !item.containsKey(parentKey)) {
                item.put(parentKey, parentId);
            }
            if (!item.containsKey(childrenKey)) {
                continue;
            }
            JSONArray childrenArray = item.getJSONArray(childrenKey);
            if (childrenArray.isEmpty()) {
                continue;
            }
            treeToList(childrenArray, valueKey, childrenKey, parentKey, item.getString(valueKey), callBack);
        }

    }

    public interface TreeDataFindChildrenItem<T> {
        void findItem(T item);
    }

}
